using System;
using System.Reflection;
using System.Web.UI;
using Nullables;

namespace Sedna.Core.Controller.Configurations
{
	/// <summary>
	/// Trnaslates Object values to Nullables and the other way round.
	/// </summary>
	public class NullablesDataTranslator: BaseDataTranslator
	{
		public enum Directions
		{
			ToObject,
			ToNullable
		}

		private Directions direction=Directions.ToObject;
		private Type nullableType=typeof(NullableDecimal);

		public Directions Direction
		{
			set { direction=value; }
		}

		public Type NullableType
		{
			set
			{
				if (!typeof(INullableType).IsAssignableFrom(value))
				{
					throw new ArgumentException(String.Format("Type {0} is not INullableType", value));
				}
				nullableType = value;
			}
		}


		public override object UnsafeTranslate(Control control, object value, Type targetType)
		{
			if (direction==Directions.ToObject)
			{
				return ToObject(value);
			}

			return ToNullable(value);
		}

		private object ToObject(object value)
		{
			if (value==null)
			{
				return null;
			}
			INullableType nullable=value as INullableType;
			if (nullable==null)
			{
				throw new ArgumentException("Value must implemenet INullableType.");
			}
			if (!nullable.HasValue)
			{
				return null;
			}
			return nullable.Value;
		}

		private object ToNullable(object value)
		{
			if (value==null)
			{
				return nullableType.GetField("Default", BindingFlags.Static|BindingFlags.Public).GetValue(null);
			}
			if (nullableType.IsInstanceOfType(value))
			{
				return value;
			}
			ConstructorInfo constructorInfo=nullableType.GetConstructor(new Type[]{value.GetType()});
			if (constructorInfo==null)
			{
				throw new NotSupportedException(String.Format("Value of type {0} cannot be converted to {1}.", value.GetType(), nullableType));
			}
			return constructorInfo.Invoke(new object[]{value});
		}

	}
}
